package com.babyte.breakpad;

import android.util.Log;

import androidx.annotation.Keep;

import com.babyte.breakpad.callback.NativeCrashCallback;
import com.babyte.breakpad.callback.NativeCrashInfoCallback;
import com.babyte.breakpad.data.CrashInfo;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.Map;


/**
 * 不能修改包名
 */
@Keep
public class BaByteBreakpad {
    private static volatile BaByteBreakpad instance;

    public static BaByteBreakpad getInstance() {
        if (instance == null) {
            synchronized (BaByteBreakpad.class) {
                if (instance == null) {
                    instance = new BaByteBreakpad();
                }
            }
        }
        return instance;
    }

    private static final int LOG_MAX_LENGTH = 20;
    private BaByteBreakpad(){
//        System.loadLibrary("breakpad");
    }
    static {
        System.loadLibrary("breakpad");
    }

    private static native void initBreakpadNative(String path, NativeCrashCallback callback);

    /**TODO 穿了dir文件保存没生效，回调也没走。。。
     * @param dir                      输出minidump文件到dir目录
     * @param nativeCrashWholeCallBack 将minidump文件路径，native层的异常信息，java层异常信息回调给开发者
     */
    public void initBreakpad(String dir, NativeCrashInfoCallback nativeCrashWholeCallBack) {
        initBreakpadNative(dir, new NativeCrashCallback() {
            @Override
            public void onCrash(String miniDumpPath, String crashInfo, String nativeThreadTrack, String crashThreadName) {
                nativeCrashWholeCallBack.onCrashInfo(
                        new CrashInfo(miniDumpPath,
                                crashInfo,
                                nativeThreadTrack,
                                getStack(crashThreadName)));

            }
        });
    }

    public void initBreakpad(NativeCrashInfoCallback nativeCrashInfoCallBack) {
        initBreakpadNative(null, new NativeCrashCallback() {
            @Override
            public void onCrash(String miniDumpPath, String crashInfo, String nativeThreadTrack, String crashThreadName) {
                nativeCrashInfoCallBack.onCrashInfo(
                        new CrashInfo(
                                null,
                                crashInfo,
                                nativeThreadTrack,
                                getStack(crashThreadName)));
            }
        });
    }

    public void formatPrint(String tag, CrashInfo info) {
        formatPrint(tag, info.getPath() != null ? info.getPath() : "not dump minidump file");
        formatPrint(tag, info.getNativeInfo());
        formatPrint(tag, info.getNativeThreadTrack());
        formatPrint(tag, info.getJvmThreadTrack());
    }

    //    private void formatPrint(String TAG, String msg) {
//        StringBuilder sb = new StringBuilder();
//        int i = 1;
//        StringReader reader= new StringReader(msg);
//        reader.readLines().forEach {
//            i++;
//            sb.appendLine(it);
//            if (i >= LOG_MAX_LENGTH) {
//                i = 1;
//                Log.e(TAG, " \n$sb");
//                sb.clear();
//            }
//        }
//        if (sb.isNotEmpty()) {
//            Log.e(TAG, " \n$sb");
//            sb.clear();
//        }
//    }
    private void formatPrint(String tag, String msg) {
        StringBuilder sb = new StringBuilder();
        int i = 1;
        try (BufferedReader reader = new BufferedReader(new StringReader(msg))) {
            String line;
            while ((line = reader.readLine()) != null) {
                i++;
                sb.append(line);
                sb.append('\n');
                if (i >= LOG_MAX_LENGTH) {
                    i = 1;
                    Log.e(tag, " \n" + sb.toString());
                    sb.setLength(0);
                }
            }
        } catch (IOException e) {
            // Handle exception or log error
            e.printStackTrace();
        }

        if (sb.length() > 0) {
            Log.e(tag, " \n" + sb.toString());
            sb.setLength(0);
        }
    }

    private String getStack(String crashThreadName) {
        StringBuilder stringBuilder = new StringBuilder();
        Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces();
        // 使用 entrySet()
        for (Map.Entry<Thread, StackTraceElement[]> entry : map.entrySet()) {
            Thread thread = entry.getKey();
            StackTraceElement[] stack = entry.getValue();
            if (thread.getName().contains(crashThreadName)) {
                stringBuilder.append("$thread").append('\n');
                for (StackTraceElement element : stack) {
                    stringBuilder.append("     at ").append(element).append('\n');
                }
            }
        }
        return stringBuilder.toString();
    }
}